﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright company="Microsoft Corp." file="AzureServiceManagementClient.cs">
//   All rights reserved. See License.txt in the project root for license information.
// </copyright>
// 
// --------------------------------------------------------------------------------------------------------------------
namespace AzureManagement
{
    using System;
    using System.Collections.Generic;
    using System.Security;
    using System.Security.Cryptography.X509Certificates;
    using System.Threading;
    using System.Threading.Tasks;

    using AzureManagement.AzureCommands;
    using AzureManagement.AzureDataContracts;

    using Common;

    using RestClient;

    public class AzureServiceManagementClient : AzureRestServiceClientBase, IAzureServiceManagementClient
    {
        public AzureServiceManagementClient(IAzureServiceContext serviceContext)
            : base(serviceContext)
        {
        }

        public async Task<string> CreateHostedService(
                                            string name, 
                                            string label, 
                                            string description, 
                                            string location, 
                                            string affinityGroup, 
                                            RequestOptions options = null)
        {
            var createHostedService = new CreateHostedService
                                          {
                                              ServiceName = name, 
                                              Label = TextHelper.EncodeToBase64(label), 
                                              Description = description, 
                                              Location = location, 
                                              AffinityGroup = affinityGroup
                                          };

            var response = await this.ExecuteCommand(() => new CreateHostedServiceCommand(this.ServiceContext as IAzureServiceContext, createHostedService), options);

            return this.GetOperationId(response);
        }

        public async Task<HostedService> GetHostedServiceProperties(string serviceName, bool embedDetails = false, RequestOptions options = null)
        {
            if (string.IsNullOrEmpty(serviceName))
            {
                throw new ArgumentException("Service name must not be null or empty.", "serviceName");
            }

            var response = await this.ExecuteCommand(() => new GetHostedServicePropertiesCommand(this.ServiceContext as IAzureServiceContext, serviceName, embedDetails), options);

            return XmlHelper.Deserialize<HostedService>(response);
        }

        public async Task<IList<HostedService>> ListHostedServices(RequestOptions options = null)
        {
            var response = await this.ExecuteCommand(() => new ListHostedServicesCommand(this.ServiceContext as IAzureServiceContext), options);

            return XmlHelper.Deserialize<HostedServices>(response).Items;
        }

        public async Task<string> DeleteHostedService(string serviceName, RequestOptions options = null)
        {
            var response = await this.ExecuteCommand(() => new DeleteHostedServiceCommand(this.ServiceContext as IAzureServiceContext, serviceName), options);

            return this.GetOperationId(response);
        }

        public async Task<string> CreateDeployment(
                                            string serviceName, 
                                            DeploymentSlots deploymentSlot, 
                                            DeploymentDetails deploymentDetails, 
                                            RequestOptions options = null)
        {
            var response = await this.ExecuteCommand(
                                            () => new CreateDeploymentCommand(
                                                                this.ServiceContext as IAzureServiceContext, 
                                                                serviceName, 
                                                                deploymentSlot, 
                                                                new CreateDeployment
                                                                {
                                                                    Name = deploymentDetails.Name, 
                                                                    PackageUrl = deploymentDetails.PackageUrl, 
                                                                    Label = TextHelper.EncodeToBase64(deploymentDetails.Label), 
                                                                    Configuration = TextHelper.EncodeToBase64(deploymentDetails.Configuration), 
                                                                    StartDeployment = deploymentDetails.StartDeployment, 
                                                                    TreatWarningsAsError = deploymentDetails.TreatWarningsAsError
                                                                }),
                                                                options);

            return this.GetOperationId(response);
        }

        public async Task<Deployment> GetDeployment(string serviceName, string deploymentName, RequestOptions options = null)
        {
            var response = await this.ExecuteCommand(() => new GetDeploymentCommand(this.ServiceContext as IAzureServiceContext, serviceName, deploymentName), options);

            return XmlHelper.Deserialize<Deployment>(response);
        }

        public async Task<Deployment> GetDeployment(string serviceName, DeploymentSlots deploymentSlot, RequestOptions options = null)
        {
            var response = await this.ExecuteCommand(() => new GetDeploymentCommand(this.ServiceContext as IAzureServiceContext, serviceName, deploymentSlot), options);

            return XmlHelper.Deserialize<Deployment>(response);
        }

        public async Task<string> DeleteDeployment(string serviceName, string deploymentName, RequestOptions options = null)
        {
            var response = await this.ExecuteCommand(() => new DeleteDeploymentCommand(this.ServiceContext as IAzureServiceContext, serviceName, deploymentName), options);

            return this.GetOperationId(response);
        }

        public async Task<string> DeleteDeployment(string serviceName, DeploymentSlots deploymentSlot, RequestOptions options = null)
        {
            var response = await this.ExecuteCommand(() => new DeleteDeploymentCommand(this.ServiceContext as IAzureServiceContext, serviceName, deploymentSlot), options);

            return this.GetOperationId(response);
        }

        public async Task<string> CreateStorageAccount(
                                                string name, 
                                                string description, 
                                                string label, 
                                                string affinityGroup, 
                                                string location, 
                                                bool geoReplication, 
                                                RequestOptions options = null)
        {
            var response = await this.ExecuteCommand(
                                                () => new CreateStorageAccountCommand(
                                                                this.ServiceContext as IAzureServiceContext, 
                                                                new CreateStorageServiceInput
                                                                {
                                                                    ServiceName = name, 
                                                                    Description = description, 
                                                                    Label = TextHelper.EncodeToBase64(label), 
                                                                    AffinityGroup = affinityGroup, 
                                                                    Location = location, 
                                                                    GeoReplicationEnabled = geoReplication
                                                                }),
                                                                options);

            return this.GetOperationId(response);
        }

        public async Task<IList<StorageService>> ListStorageAccounts(RequestOptions options = null)
        {
            var response = await this.ExecuteCommand(() => new ListStorageAccountCommand(this.ServiceContext as IAzureServiceContext), options);

            return XmlHelper.Deserialize<StorageServices>(response).Items;
        }

        public async Task<StorageServiceKeys> GetStorageAccountKeys(string storageAccountName, RequestOptions options = null)
        {
            var response = await this.ExecuteCommand(() => new GetStorageAccountKeysCommand(this.ServiceContext as IAzureServiceContext, storageAccountName), options);

            return XmlHelper.Deserialize<StorageService>(response).StorageServiceKeys;
        }

        public async Task<string> DeleteStorageAccount(string storageAccountName, RequestOptions options = null)
        {
            var response = await this.ExecuteCommand(() => new DeleteStorageAccountCommand(this.ServiceContext as IAzureServiceContext, storageAccountName), options);

            return this.GetOperationId(response);
        }

        public async Task<string> AddServiceCertificate(string serviceName, X509Certificate2 certificate, string certificatePassword, RequestOptions options = null)
        {
            var content = certificate.Export(X509ContentType.Pfx, certificatePassword);

            var certificateFile = new CertificateFile
                                      {
                                          Data = Convert.ToBase64String(content), 
                                          CertificateFormat = "pfx", 
                                          Password = SecurityElement.Escape(certificatePassword)
                                      };

            var response = await this.ExecuteCommand(() => new AddServiceCertificateCommand(this.ServiceContext as IAzureServiceContext, serviceName, certificateFile), options);

            return this.GetOperationId(response);
        }

        public async Task<IList<Certificate>> ListServiceCertificates(string serviceName, RequestOptions options = null)
        {
            var response = await this.ExecuteCommand(() => new ListServiceCertificatesCommand(this.ServiceContext as IAzureServiceContext, serviceName), options);

            return XmlHelper.Deserialize<Certificates>(response).Items;
        }

        public async Task<string> DeleteServiceCertificate(
            string serviceName, string thumbprintAlg, string thumbprint, RequestOptions options = null)
        {
            var response = await this.ExecuteCommand(() => new DeleteServiceCertificateCommand(this.ServiceContext as IAzureServiceContext, serviceName, thumbprintAlg, thumbprint), options);

            return this.GetOperationId(response);
        }

        public async Task<Operation> GetOperationStatus(string operationId, RequestOptions options = null)
        {
            var response = await this.ExecuteCommand(() => new GetOperationStatusCommand(this.ServiceContext as IAzureServiceContext, operationId), options);

            return XmlHelper.Deserialize<Operation>(response);
        }

        public Operation WaitOperationForCompletion(string operationId, TimeSpan timeout, RequestOptions options = null)
        {
            var start = DateTime.Now;

            Operation operation = null;
            var operationStatus = OperationStatuses.InProgress;
            while (operationStatus == OperationStatuses.InProgress)
            {
                operation = this.GetOperationStatus(operationId, options).Result;

                Thread.Sleep(1000);

                if (DateTime.Now.Subtract(start) >= timeout)
                {
                    throw new TimeoutException();
                }

                operationStatus = operation.Status;
            }

            return operation;
        }
    }
}